// EventLoop.cpp
#include "EventLoop.hpp"
#include "Channel.hpp"
#include "Poller.h"

#include <unistd.h>
#include <cassert>
#include <iostream>

namespace {
int createEventfd() {
    int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    if (evtfd < 0) {
        perror("eventfd creation failed");
        abort();
    }
    return evtfd;
}
}

EventLoop::EventLoop()
    : looping_(false),
      quit_(false),
      threadId_(::gettid()),
      poller_(std::make_unique<Poller>()),
      wakeupFd_(createEventfd()),
      wakeupChannel_(std::make_unique<Channel>(this, wakeupFd_)) {

    wakeupChannel_->setReadCallback(std::bind(&EventLoop::handleRead, this));
    wakeupChannel_->enableReading();
}

EventLoop::~EventLoop() {
    ::close(wakeupFd_);
}

void EventLoop::loop() {
    looping_ = true;

    while (!quit_) {
        activeChannels_.clear();
        poller_->poll(10000, activeChannels_);

        for (auto channel : activeChannels_) {
            channel->handleEvent();
        }

        doPendingFunctors();
    }

    looping_ = false;
}

void EventLoop::quit() {
    quit_ = true;
    if (::gettid() != threadId_) {
        wakeup();
    }
}

void EventLoop::runInLoop(Functor cb) {
    if (::gettid() == threadId_) {
        cb();
    } else {
        queueInLoop(std::move(cb));
    }
}

void EventLoop::queueInLoop(Functor cb) {
    {
        std::lock_guard<std::mutex> lock(mutex_);
        pendingFunctors_.emplace_back(std::move(cb));
    }

    if (::gettid() != threadId_ || callingPendingFunctors_) {
        wakeup();
    }
}

void EventLoop::wakeup() {
    uint64_t one = 1;
    ssize_t n = ::write(wakeupFd_, &one, sizeof(one));
    if (n != sizeof(one)) {
        perror("EventLoop::wakeup() write error");
    }
}

void EventLoop::handleRead() {
    uint64_t one = 1;
    ssize_t n = ::read(wakeupFd_, &one, sizeof(one));
    if (n != sizeof(one)) {
        perror("EventLoop::handleRead() read error");
    }
}

void EventLoop::doPendingFunctors() {
    std::vector<Functor> functors;
    callingPendingFunctors_ = true;

    {
        std::lock_guard<std::mutex> lock(mutex_);
        functors.swap(pendingFunctors_);
    }

    for (const Functor& func : functors) {
        func();
    }

    callingPendingFunctors_ = false;
}

void EventLoop::updateChannel(Channel* channel) {
    poller_->updateChannel(channel);
}

void EventLoop::removeChannel(Channel* channel) {
    poller_->removeChannel(channel);
}
